home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
bit
/
src
/
iutil.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
54KB
|
2,363 lines
/*
* $Id: iutil.c,v 0.91 1994/02/20 00:53:07 zhao Pre-Release $
*
*. This file is part of BIT shareware package. After the two weeks of
* free evaluation period, you are encouraged (required) to register
* your copy for a small registration fee, which is $35 for personal use
* and $50 for commercial, government and institutional use.
*
* Copyright(c) 1993, 1994 by T.C. Zhao.
* All rights reserved.
*
* Permission to use, copy, and distribute this software in its entirety
* for non-commercial purposes is hereby granted, provided that the
* above shareware and copyright notices and this permission notice
* appear in all copies and their documentation.
*
* This software may be modified for your own use, but modified versions
* may not be distributed without prior consent of the author.
*
* This software is provided "as is" without expressed or implied
* warranty of any kind.
*
*.
*
* utilities dealing with memory allocations and image manipulations
*/
#if !defined(lint) && defined(F_ID)
char *id_iutil = "$Id: iutil.c,v 0.91 1994/02/20 00:53:07 zhao Pre-Release $";
#endif
#include "bit.h"
#include "extern.h"
#include "dmalloc.h"
/****************** Defines and limits *****************************/
#define CONVERT_TYPE /* controls the rgb->gray lookup table */
#include "lookup.h"
/*****************************************************************
* Gather all recognized format info which is output by function out
* defined as void (*out)(char *)
******************************************************************/
void
enumerate_formats(void (*out) (const char *))
{
char lstr[100];
const char *fmt;
IMG_IO *ios = img_io + totalfmt, *io = img_io;
if (!out)
{
M_warn("EnumerateFormats", "Bad output function");
return;
}
/* write only +, read only - */
while (io < ios)
{
fmt = (!io->load) ? "%s(%s)+" :
(io->dump ? "%s(%s)" : "%s(%s)-");
sprintf(lstr, fmt, io->info, io->key);
out(lstr);
io++;
}
}
/***************************************************************
* Gather all formats that are capable of writing
**************************************************************/
void
init_w_formats(void)
{
if (!totalwrite)
{
register IMG_IO *ios = img_io + totalfmt, *io = img_io;
while (io < ios)
{
if (io->dump)
wio[totalwrite++] = io;
io++;
}
}
}
/****************************************************************
* Insert a pixel into a matrix. Pixel is represented by
* a 4-element vector, the first 3 is RGB component and the
* 4th is the colorindex if the image is in CI
****************************************************************/
void
pack_pixel(IPTR im, void *m, int py, int px, int pc[])
{
if (IS_CI(im))
{
((ci_t **) m)[py][px] = pc[3];
}
else
{
((rgba_t **) m)[py][px] = Pack(pc[0], pc[1], pc[2]);
}
}
/*************************************************************
* pick a pixel from a matrix
**************************************************************/
void
pick_pixel(IPTR im, void *m, int py, int px, int pc[])
{
int ci;
if (IS_CI(im))
{
pc[3] = ci = ((ci_t **) m)[py][px];
pc[0] = im->cmap->ct[0][ci];
pc[1] = im->cmap->ct[1][ci];
pc[2] = im->cmap->ct[2][ci];
}
else
{
rgba_t p = ((rgba_t **) m)[py][px];
Unpack(p, pc[0], pc[1], pc[2]);
pc[3] = 0;
}
}
/****************************************************
* Routines that deal with separate RGB matrices
***************************************************/
void
img_free_rgbmem(IPTR im)
{
free_mat(im->pc[0]);
if (im->pc[0] != im->pc[1])
{ /* if equal, must be gray */
free_mat(im->pc[1]);
free_mat(im->pc[2]);
}
im->pc[0] = im->pc[1] = im->pc[2] = 0;
}
void
img_replace_rgb(IPTR im, pc_t **r, pc_t **g, pc_t **b)
{
if (im->pc[0] != r)
img_free_rgbmem(im);
im->pc[0] = r;
im->pc[1] = g;
im->pc[2] = b;
}
/****************************************************************
* get_rgbmem must get RM, GM, BM regardless of the image type.
* otherwise, need to change many routines regarding R,G,B matrices
***************************************************************/
int
img_get_rgbmem(IPTR im)
{
pc_t **r, **g = 0, **b = 0;
if (!(r = get_mat(im->h, im->w, sizeof(pc_t))) ||
! (g = get_mat(im->h, im->w, sizeof(pc_t))) ||
! (b = get_mat(im->h, im->w, sizeof(pc_t))))
{
free_mat(r);
free_mat(g);
free_mat(b);
return -1;
}
img_replace_rgb(im, r, g, b);
return 0;
}
/*********************************************************
* routines that deal with raster mem
*********************************************************/
void
img_free_rastermem(IPTR im)
{
if (im->size > 2 && im->mraster)
{
if (((char **) im->mraster)[0] != (char *) im->raster)
{
Bark("FreeRasterMem", "memory corrupt");
return;
}
free_mat(im->mraster);
im->size = im->ok = 0;
im->mraster = 0;
}
return;
}
extern void exit(int);
IPTR
get_mem_imgptr(void)
{
const char *fn = "GetMemIptr";
IPTR p = calloc(1, sizeof(*p));
if (!p)
{
M_err(fn, mfailed);
if (win_id > 0)
Bark(fn, mfailed);
clean_up();
}
/* initialize all pointers and non-zero default */
p->type = T_FLEX;
p->mraster = p->raster = 0;
p->cmap = 0;
p->pc[0] = p->pc[1] = p->pc[2] = 0;
p->fp = 0;
p->aspect = 1000;
p->comm = 0;
return p;
}
/*******************************************************
* Colormaps
*******************************************************/
/* Demand a colormap. If unsuccesful, quit */
CMPTR
get_mem_cmap(void)
{
CMPTR p = calloc(1, sizeof(*p));
if (!p)
{
M_err("GetMemCmap", mfailed);
if (win_id > 0)
Bark("GetMemCmap", mfailed);
clean_up();
}
p->colors = p->ucolors = p->packed = 0;
return p;
}
void
free_mem_imgptr(IPTR p)
{
if (p)
{
p->ok = p->size = 0;
p->mraster = 0;
free(p);
}
}
void
free_mem_cmap(CMPTR p)
{
free(p);
}
int
img_get_rastermem(IPTR im)
{
register void *p, **pp;
register size_t esize = 0;
const char *fn = "GetRasterMem";
/* this theoretically should never happen */
if (!im)
{
Bark(fn, "Something is very wrong");
return -1;
}
if (im->type == T_FLEX || im->w < 1 || im->h < 1)
{
Bark(fn, "%s: Bad %s description", im->ifile,
im->io ? im->io->key : "???");
return -1;
}
if (IS_CI(im))
im->esize = esize = sizeof(ci_t);
else if (IS_CPACK(im))
im->esize = esize = sizeof(rgba_t);
else
{
Bark(fn, "Bad Image type");
clean_up();
}
img_free_rastermem(im);
if (!(p = get_mat(im->h, im->w, esize)))
{
im->size = 0;
return -1;
}
pp = p;
im->mraster = pp;
im->raster = pp[0];
im->size = esize * im->w * im->h;
return 0;
}
/*
* this function is really overkill, im->ok should be able to indicate if the
* image is ready. Be on the safe side, also might catch unexpected errors
*/
int
image_ready(IPTR im, const char *func)
{
int p = (im != 0 && im->ok
&& im->size > 1
&& im->w >= 1
&& im->mraster
&& im->raster
&& (IS_CPACK(im) || IS_CI(im)));
if (!p)
Bark(func, "No image or bad type");
return p;
}
/*
* duplicate an image raster;
*/
int
img_dup_raster(IPTR new, IPTR old)
{
const char *fn = "RasterDup";
if (!image_ready(old, fn) || !new)
return -1;
new->w = old->w;
new->h = old->h;
new->type = old->type;
return (img_get_rastermem(new) < 0 ||
memcpy(new->raster, old->raster, new->size) == 0) ? -1 : 0;
}
void
img_free_cmap(IPTR im)
{
if (im && im->cmap)
{
free_mem_cmap(im->cmap);
im->cmap = 0;
}
}
/*****************************************************************
* duplicate a colormap (free the old one if present)
*****************************************************************/
int
img_dup_cmap(IPTR new, IPTR old)
{
if (!image_ready(old, "dup_raster") || !new)
return -1;
img_free_cmap(new);
new->cmap = get_mem_cmap();
return memcpy(new->cmap, old->cmap, sizeof(*old->cmap)) ? 0 : -1;
}
/*******************************************************************
* duplicate an image and return the new image pointer
********************************************************************/
IPTR
img_dup(IPTR old)
{
IPTR new;
int ok = -1;
show_busy("ImageDup ...");
new = get_mem_imgptr();
if (memcpy(new, old, sizeof(*old)) != 0)
{
new->size = new->ok = 0;
new->mraster = new->raster = 0;
new->cmap = 0;
ok = !(img_dup_cmap(new, old) || img_dup_raster(new, old));
new->ok = ok;
}
end_busy();
return ok ? new : 0;
}
void
free_image_mem(IPTR im)
{
if (!im)
return;
img_free_cmap(im);
img_free_rastermem(im);
img_free_rgbmem(im);
}
/********************************************************************
* free everything that is associated with the image structure img
*
* free_rastermem
* free_cmap
* free_imgptr
*********************************************************************/
void
free_image(IPTR im)
{
if (im)
{
free_image_mem(im);
free_mem_imgptr(im);
}
}
/***********************************************************************
* get the border offset into the image. Note MAXC is the maximum no. of
* colors on one line(horizontal or vertical) can have to be considered
* to be a border line. Coordinates are from Left to Right and Bottom to Top.
*********************************************************************/
#define MAXC 4
#define DOIT(type,nouse) \
register type **mras= im->mraster, *ras, first; \
do { \
ok= 1; ras= mras[h - 1]; \
for (j= h - 1; j >= 0 && ok; j--,(*t)++,ras= mras[j]){ \
first= *ras++; \
for (i= 1 ; i < w && first== *ras;i++, ras++) \
; \
ok= i== w; \
} \
h -= *t; ok= 1; ras= mras[0]; \
for (j= 0; j < h && ok; j++, (*b)++, ras= mras[j]) { \
for (i= 0, first= *ras; ++i < w && first== *ras++;) \
; \
ok= i== w; \
} \
ok= 1; \
for (*l= 0; *l < w && ok; (*l)++) { \
first= mras[*b][*l]; \
for (j= *b; j < h && ok; j++) \
ok= first== mras[j][*l]; \
} \
ok= 1; \
for (i= w - 1; i > *l && ok; i--, (*r)++) { \
first= mras[*b][i]; \
for (j= *b; j < h && ok; j++) \
ok= first== mras[j][i]; \
} \
} while (ZERO)
int
img_get_border(IPTR im, int *b, int *l, int *t, int *r, int n)
{
register int i, j, ok, h, w;
if (!image_ready(im, "Border"))
return -1;
*t = *l = *r = *b = 0;
h = im->h;
w = im->w;
show_busy("PleaseWait ...");
if (IS_CPACK(im))
{ /* in RGBA format */
DOIT(rgba_t, n);
}
else
{
DOIT(ci_t, n);
}
end_busy();
return 0;
}
/****************************************************************
* Note not true histograms, rather histogram for RGB separately.
* Histogram is not normalized.
****************************************************************/
int
img_get_histogram(IPTR im)
{
Hist_t *hist = im->cmap->p_h.hist;
long total = im->w * im->h;
int i, j;
double ave[4];
#ifdef MTRACE
M_trace("Histogram", "Entering");
#endif
show_busy("GettingHist ...");
(void) memset(hist, 0, sizeof(Hist_t) * 4);
im->cmap->packed = 0; /* overwrite packed arrays */
if (IS_CPACK(im))
{
register rgba_t *rgba = im->raster, *rs;
register pc_t r;
if (IS_GRAY(im))
{
for (rs = rgba + total; rgba < rs; rgba++)
{
r = (*rgba & PCMAXV);
/* check overflows */
if (++(hist[3][r]) == 0)
(hist[3][r])--;
}
}
else
{
register pc_t g, b;
for (rs = rgba + total; rgba < rs; rgba++)
{
Unpack(*rgba, r, g, b);
if (++(hist[0][r]) == 0)
(hist[0][r])--;
if (++(hist[1][g]) == 0)
(hist[1][g])--;
if (++(hist[2][b]) == 0)
(hist[2][b])--;
++(hist[3][RGB2GRAY(r, g, b)]);
}
}
}
else
{ /* in cmap */
register ci_t *ci = im->raster, *cis;
register CMPTR m = im->cmap;
register pc_t *r = m->ct[0], *g = m->ct[1], *b = m->ct[2];
if (IS_GRAY(im))
{
for (cis = ci + total; ci < cis; ci++)
{
++(hist[3][r[*ci]]);
}
}
else
{
for (cis = ci + total; ci < cis; ci++)
{
++(hist[0][r[*ci]]);
++(hist[1][g[*ci]]);
++(hist[2][b[*ci]]);
++(hist[3][RGB2GRAY(r[*ci], g[*ci], b[*ci])]);
}
}
}
if (IS_GRAY(im))
{
if (memcpy(hist[0], hist[3], sizeof(Hist_t)) == 0 ||
memcpy(hist[1], hist[3], sizeof(Hist_t)) == 0 ||
memcpy(hist[2], hist[3], sizeof(Hist_t)) == 0)
{
Bark("Histogram", "memcpy failed");
return -1;
}
}
/* get peak value and average */
for (j = 0; j < 4; j++)
{
ave[j] = 0.0;
hist[j][PCMAX] = 0.0;
for (i = 0; i < PCMAX; i++)
{
if (hist[j][i] > hist[j][PCMAX])
{
hist[j][PCMAX] = hist[j][i];
hist[j][PCMAX + 2] = i;
}
ave[j] += hist[j][i] * i;
}
hist[j][PCMAX + 1] = (ave[j] / total);
}
end_busy();
#ifdef MTRACE
M_trace("Histogram", "Exit");
#endif
return 0;
}
/******************************************************************
* modify raster according the input function: f(x) Basically a
* one-to-one mapping.
*******************************************************************/
void
modify_rgb(register rgba_t *ras, long n, register pc_t *r,
register pc_t *g, register pc_t *b)
{
register int rr, gg, bb;
register rgba_t *rs;
if (r == g)
{ /* must be grayscale images */
for (rs = ras + n; ras < rs; ras++)
{
rr = *ras & PCMAXV;
rr = *(r + rr);
*ras = Pack(rr, rr, rr);
}
}
else
{
for (rs = ras + n; ras < rs; ras++)
{
Unpack(*ras, rr, gg, bb);
rr = r[rr];
gg = g[gg];
bb = b[bb];
*ras = Pack(rr, gg, bb);
}
}
}
void
modify_cpack_pixel(register rgba_t *ras, long total,
register rgba_t old, register rgba_t new,
register int sw)
{
register rgba_t *ra;
for (ra = ras + total; ras < ra; ras++)
{
if (*ras == old)
*ras = new;
else if (sw && (*ras == new))
*ras = old;
}
}
/*******************************************************************
* Modifies all rasters according to mapping given by nlut
*********************************************************************/
void
modify_all_ci(register ci_t *ras, long total, register ci_t *nlut)
{
register ci_t *ra = ras + total;
for (; ras < ra; ras++)
*ras = nlut[*ras];
}
/***********************************************************************
* this can be written as special case of modify_all_ci with 2 entris
* that are not null transformations lut[i] == i.
* But for simple swap, this is faster.
********************************************************************/
void
modify_ci_pixel(register ci_t *ras, long total,
register ci_t old, register ci_t new, register int sw)
{
register ci_t *ra = ras + total;
for (; ras < ra; ras++)
{
if (*ras == old)
*ras = new;
else if (sw && *ras == new)
*ras = old;
}
}
/* **************************************************************
* replace all pixels that have oldp with newp. If sw is true
* switch old and new
******************************************************************/
void
img_modify_pixel(IPTR im, int oldp[], int newp[], int sw)
{
show_busy("");
if (IS_CPACK(im))
{
modify_cpack_pixel(im->raster, im->w * im->h,
Pack(oldp[0], oldp[1], oldp[2]),
Pack(newp[0], newp[1], newp[2]), sw);
}
else
{
modify_ci_pixel(im->raster, im->w * im->h,
oldp[3], newp[3], sw);
}
end_busy();
}
/***************************************************************
* Colormap routines
***************************************************************/
void
set_colmap(int n, pc_t *r, pc_t *g, pc_t *b)
{
register int i;
for (i = 0; i < n; i++, r++, g++, b++)
mapcolor(i, *r, *g, *b);
}
void
set_cmap(CMPTR m)
{
set_colmap(m->colors, m->ct[0], m->ct[1], m->ct[2]);
}
void
get_colmap(int n, pc_t *r, pc_t *g, pc_t *b)
{
register int i;
short x, y, z;
for (i = 0; i < n; i++, *r++ = x, *g++ = y, *b++ = z)
getmcolor(i, &x, &y, &z);
}
void
set_cmap_entry(CMPTR m, int fc[], int ci)
{
int i;
if (ci >= MAXCML)
{
M_warn("SetCmapEntry", "Bad input");
return;
}
for (i = 0; i < 3; i++)
m->ct[i][ci] = fc[i];
m->packed = 0;
}
void
get_cmap_entry(CMPTR m, int fc[], int ci)
{
int i;
if (ci >= MAXCML)
{
M_warn("GetCmapEntry", "Bad input");
ci = 0;
}
for (i = 0; i < 3; i++)
fc[i] = m->ct[i][ci];
}
/*
* modify a colormap
*/
void
modify_cmap(CMPTR m, pc_t *r, pc_t *g, pc_t *b)
{
register pc_t *pc1 = m->ct[0], *pc2 = m->ct[1], *pc3 = m->ct[2];
register pc_t *pcs;
for (pcs = pc1 + m->colors; pc1 < pcs; pc1++, pc2++, pc3++)
{
*pc1 = r[*pc1];
*pc2 = g[*pc2];
*pc3 = b[*pc3];
}
m->packed = 0;
}
void
pack_cmap(CMPTR m)
{
register pc_t *r = m->ct[0], *g = m->ct[1], *b = m->ct[2];
register pc_t *rs;
register rgba_t *rgba = m->p_h.rgba;
if (m->packed > 0)
return;
for (rs = r + m->colors; r < rs; r++, g++, b++, rgba++)
*rgba = Pack(*r, *g, *b);
m->packed = 1;
}
void
pack_cmap_g(CMPTR m)
{
register pc_t *r = m->ct[0], *g = m->ct[1], *b = m->ct[2];
register pc_t *gr = m->ct[3], *rs;
for (rs = r + m->colors; r < rs; r++, g++, b++, gr++)
*gr = RGB2GRAY(*r, *g, *b);
}
/* find out the no. of uniq colors in a colormap */
int
cmap_ucolors(register CMPTR m, register pc_t *lut)
{
register int i, j, k;
for (i = k = 0; i < m->colors; i++)
lut[i] = i;
pack_cmap(m);
for (i = 0; i < m->colors; i++)
{
for (j = i + 1; j < m->colors; j++)
{
if (lut[j] == j && (m->p_h.rgba[i] == m->p_h.rgba[j]))
{
lut[j] = lut[i];
k++;
}
}
}
return m->ucolors = m->colors - k;
}
/********************************************************************
* Remove redundant entries from colormap and their index from image.
* Further squeeze is possible if we actually check if all entries in
* the map are actually used by examing the histograms
*********************************************************************/
void
squeeze_image(IPTR im)
{
pc_t lutable[PCMAX];
register CMPTR m = im->cmap;
register pc_t *lut = lutable;
register int i, j, k = 0;
/*
* cmap_ucolors will return a lookup table with entries lut[m] = n; with
* m > n, meaning mth entry is not unique, and is the same as nth entry.
* Also only worthwhiel to squeeze if we can save at least 1bit
*/
if ((i = cmap_ucolors(m, lut)) <= m->colors / 2)
{
int tnlut[PCMAX];
register ci_t *ci = im->raster, *cis;
register CMPTR mm = get_mem_cmap();
register int *nlut = tnlut;
mm->ct[0][0] = m->ct[0][0];
mm->ct[1][0] = m->ct[1][0];
mm->ct[2][0] = m->ct[2][0];
nlut[k++] = 0;
for (i = 1; i < m->colors; i++)
{
j = lut[i];
if (j == i) /* uniq */
{
mm->ct[0][k] = m->ct[0][i];
mm->ct[1][k] = m->ct[1][i];
mm->ct[2][k] = m->ct[2][i];
nlut[i] = k++;
}
else
{
/* redundant, and j is smaller than i */
nlut[i] = nlut[j];
}
}
if (k != m->ucolors)
{
Bark("Squeeze", "Something is wrong %d %d", k, m->ucolors);
free_mem_cmap(mm);
return;
}
#ifdef MDEBUG
for (i = 0; i < m->colors; i++)
M_debug("Squeeze", "lut[%d]=%d nlut=%d\n", i, lut[i], nlut[i]);
#endif
/* change the index */
for (cis = ci + im->w * im->h; ci < cis; ci++)
*ci = nlut[*ci];
im->colors = mm->colors = mm->ucolors = m->ucolors;
im->cmap = mm;
im->cmap->packed = 0;
free_mem_cmap(m);
im->io->display(im, 0, 0);
}
update_color_info(im);
}
/* this one should be completely rewritten */
int
cmap_closematch(CMPTR m, int r, int g, int b)
{
register int dr, dg, db;
register unsigned long mindiff, cdiff;
register int ci = 0, i;
mindiff = ~0;
for (i = 0; i < m->colors; i++)
{
dr = r - m->ct[0][i];
dg = g - m->ct[1][i];
db = b - m->ct[2][i];
cdiff = (3 * (dr * dr) + 4 * (dg * dg) + 2 * (db * db));
if (cdiff < mindiff)
{
mindiff = cdiff;
ci = i;
}
/* special provision for exact match */
if (cdiff == 0)
goto exact;
}
exact:
return ci;
}
/*****************************************************************
* need to save the original colormap when program starts and
* re-install the map when program quits. MAXMAP is 4096, but only
* need to save and restore the map entries used (0--MAXCML)
*****************************************************************/
#define MAXMAP MAXCML
static pc_t sysmap[3][MAXMAP];
static int sysmap_ok;
void
get_sysmap(void)
{
long win;
noport();
win = winopen("");
get_colmap(MAXMAP, sysmap[0], sysmap[1], sysmap[2]);
winclose(win);
sysmap_ok = 1;
}
void
set_sysmap(int p)
{
long win;
if (sysmap_ok)
{
noport();
win = winopen("");
set_colmap(MAXMAP, sysmap[0], sysmap[1], sysmap[2]);
winclose(win);
}
if (p)
print_before_exit();
}
/*********************************************************************
* get a rectangular piece of image. (x,y) is the screen location
* directly comparable to im->xi and im->yi, i.e., it is relative
* to window
********************************************************************/
void *
get_subimage(IPTR im, int x, int y, int w, int h)
{
const char *fn = "Get_subimage";
if (!image_ready(im, fn))
return 0;
if (x < im->xi || y < im->yi || h <= 0 || w <= 0 ||
(x + w - 1) > im->xf || (y + h - 1) > im->yf)
{
Bark(fn, "Bad requested region");
return 0;
}
/* now everything is ok. Do it */
return get_submat(im->mraster, im->h, im->w,
y - im->yi, x - im->xi, h, w, im->esize);
}
/*********************************************************************
* Similar to get_subimage, but will never fail: if requested
* region is outside the image, it will be filled by whatever
* in the framebuffer. It will fail, however, if illogical
* value such as w < 0 || h < 0 is passed
**********************************************************************/
void *
no_fail_get_subimage(IPTR im, int x, int y, int w, int h)
{
char **ret;
if (w <= 0 || h <= 0)
{
Bark("NoFailGetSubImage", "bad w and/or h");
return 0;
}
if (x < im->xi || y < im->yi ||
(x + w - 1) > im->xf || (y + h - 1) > im->yf)
{
int ux, uy, uw, uh;
rgba_t fillc = (IS_CPACK(imgptr) ? background_color : BKINDEX);
ret = get_mat(h, w, im->esize);
/* initialize it to background color */
init_mat(ret, h, w, im->esize, fillc);
/* need to replace the union with the piece from raster */
ux = Max(im->xi, x);
uy = Max(im->yi, y);
uw = Min(im->xf, (x + w - 1)) - ux + 1;
uh = Min(im->yf, (y + h - 1)) - uy + 1;
if (uw > 0 && uh > 0) /* union exists */
{
void *p = get_subimage(im, ux, uy, uw, uh);
int oop = get_mat_op();
/* Insert sm into framebuffer matrix. Always in NOOP mode */
set_mat_op(O_NONE);
put_submat(ret, h, w, p,
uy - y, ux - x, uh, uw, im->esize);
set_mat_op(oop);
free_mat(p);
}
}
else
{
/* within image */
ret = get_subimage(im, x, y, w, h);
}
return ret;
}
int
put_subimage(IPTR im, void *m, const Rect_t * r, int warn)
{
static const char *fn = "PutSubImage";
const Rect_t *o;
void *sm;
if (!image_ready(im, fn))
return -1;
if (!(o = union_rect(r, img_rect(im))))
{
if (warn)
Bark(fn, "Bad requested region");
return -1;
}
if (equal_rect(r, o))
{
return put_submat(im->mraster, im->h, im->w,
m, r->y - im->yi, r->x - im->xi, r->h, r->w, im->esize);
}
/* clip */
sm = get_submat(m, r->h, r->w,
o->y - r->y, o->x - r->x, o->h, o->w, im->esize);
put_submat(im->mraster, im->h, im->w, sm,
o->y - im->yi, o->x - im->xi, o->h, o->w, im->esize);
free_mat(sm);
return 0;
}
/***********************************************************************
* given several keys, say ppm, pgm and pbm, check if any of the keys has
* the same type as the current image, if there is, return the index into
* the img_io, else if no match can be found, return the first match.
* Therefore, the caller should always place the keys in decreasing order
* in generality, i.e, ppm, pgm and pbm, NEVER pbm, pgm and ppm
**********************************************************************/
int
best_format(IPTR im, char *const *keys)
{
char *const *q = keys;
int i, first = -1;
int bestsofar = totalfmt + 1; /* negative match, positive best */
int best;
if (!*q || !im->ok)
{
#ifdef MDEBUG
M_debug("BestFmt", "Bad input");
#endif
return -1;
}
do
{
/* go thru all images and check for key match */
for (i = 0; i < totalfmt && strcasecmp(img_io[i].key, *q); i++)
;
/* we have found a match at i, check if type OK */
if (i < totalfmt)
{ /* match */
bestsofar = (im->type == img_io[i].type ||
img_io[i].type == T_FLEX) ? i : -i;
/* remember the first match */
if (first < 0)
first = i; /* first match */
}
#ifdef MDEBUG
else
{
M_debug("BestFmt", "%s not found", *q);
}
#endif
}
while (*++q && (bestsofar < 0 || bestsofar > totalfmt));
/* the best one */
best = (bestsofar > totalfmt) ? -1 : (bestsofar >= 0 ? bestsofar : first);
M_info("BestFmt", "1stReq %s, Best %s",
keys[0], (best >= 0) ? img_io[best].key : "none");
return best;
}
/***********************************************************************
* image type conversions
* quantization is in a separate file due to its size
***********************************************************************/
#include "lookup.h" /* lookup tables */
/**** given RGB, get the packed pixel for display *****/
static void
rgb_to_cpack(register rgba_t *p, register pc_t *r,
register pc_t *g, register pc_t *b, long sz)
{
register pc_t *rs;
if (r == g)
{ /* grayscale */
for (rs = r + sz; r < rs; r++)
*p++ = Pack(*r, *r, *r);
}
else
{
for (rs = r + sz; r < rs; r++, g++, b++)
*p++ = Pack(*r, *g, *b);
}
}
int
img_rgb_to_cpack(IPTR im)
{
register pc_t *r, *g, *b;
register long total = im->h * im->w;
if (!im->pc[0])
{
Bark("Packing", "Bad RGB matrix");
return -1;
}
r = im->pc[0][0];
g = im->pc[1][0];
b = im->pc[2][0];
im->type = (IS_GRAY(im) || r == g) ? T_GRAY : T_RGBA;
if (img_get_rastermem(im) >= 0)
{
if (im->type == T_RGBA)
{
rgb_to_cpack(im->raster, r, g, b, total);
}
else
{
rgb_to_cpack(im->raster, r, r, r, total);
}
im->ok = 1;
}
return im->ok ? 0 : -1;
}
/******* given a cpack, take it apart to get RGB *******/
static int
cpack_to_rgb(IPTR im)
{
register pc_t *r, *g, *b;
register rgba_t *rgba, *rs;
register int total = im->w * im->h;
int status;
if ((status = img_get_rgbmem(im)) >= 0)
{
rgba = im->raster;
r = im->pc[0][0];
g = im->pc[1][0];
b = im->pc[2][0];
for (rs = rgba + total; rgba < rs; r++, g++, b++, rgba++)
{
CPACK2RGB(*rgba, *r, *g, *b);
}
}
return status;
}
/******** given a cmap img, get RGB triplets *********/
static int
cmap_to_rgb(IPTR im)
{
register pc_t *r, *g, *b;
register pc_t *mr, *mg, *mb;
register ci_t *ras = im->raster, *rs;
register int total = im->w * im->h;
int status;
if ((status = img_get_rgbmem(im)) >= 0)
{
mr = im->cmap->ct[0];
mg = im->cmap->ct[1];
mb = im->cmap->ct[2];
r = im->pc[0][0];
g = im->pc[1][0];
b = im->pc[2][0];
for (rs = ras + total; ras < rs; ras++)
{
*r++ = mr[*ras];
*g++ = mg[*ras];
*b++ = mb[*ras];
}
}
else
{
Bark("cmap2cpack", mfailed);
}
return status;
}
/***** get RGB triplets from an image *****/
int
img_get_rgb(IPTR im)
{
return (!im || !im->raster) ? -1 :
(IS_CI(im) ? cmap_to_rgb : cpack_to_rgb) (im);
}
/***** fill some fields of image structure ******/
int
fill_image_struct(IPTR im, void *m, int h, int w, IMG_TYPE t)
{
if (!m || w < 1 || h < 1)
{
M_err("ImageFill", "Bad input");
return -1;
}
/*
* need to make sure m and im->mraster are different before messing with
* im->mraster
*/
if (im->mraster != m)
img_free_rastermem(im);
im->w = w;
im->h = h;
im->mraster = m;
im->raster = ((char **) m)[0];
/* get the pixel size */
if (t == T_CMAP)
im->esize = sizeof(ci_t);
else if (t == T_GRAY)
im->esize = sizeof(gray_t);
else if (t == T_RGBA)
im->esize = sizeof(rgba_t);
else if (t == T_GMAP)
im->esize = sizeof(ci_t);
else if (t == T_BW)
im->esize = sizeof(bw_t);
else
return -1;
im->size = im->esize * w * h;
im->ok = 1;
im->type = t;
update_image_info(im);
return 0;
}
/** Create an image of solid colors *********/
IPTR
create_image(const char *name, int type, int w, int h, rgba_t col)
{
IPTR im;
if (w <= 0 || h <= 0)
{
M_err("ImageCreate", "Bad arg w=%d h=%d\n", w, h);
return 0;
}
(im = get_mem_imgptr())->cmap = get_mem_cmap();
im->type = type;
im->w = w;
im->h = h;
/* fill the raster */
img_get_rastermem(im);
init_mat(im->mraster, im->h, im->w, im->esize, col);
im->ok = 1;
strcpy(im->ifile, (name && *name) ? name : "untitled");
im->info = "Internal";
im->key = "?";
im->io = &img_io[0];
im->io->display = Generic_display;
return im;
}
/********************************************************************
* Convert from colormapped image to RGBA
*
* the mask is important since the mapped image might be a result of
* screen read and if writemask is not set properly (not set at all
* at this moment), the index will be full 12bits. Only the bits
* with mask are meaningful
**********************************************************************/
static int
cmap_to_cpack(IPTR im)
{
/*
* can't simply use no. of colors as masks because it might not be 2^n,
* e.g., sunraster file
*/
int p = power_of_2(im->cmap->colors);
register ci_t mask = (p > MAXCML ? MAXCML : p) - 1;
register ci_t *ci = im->raster, *cis;
register rgba_t *ras, *mp, **m;
if (!(m = get_mat(im->h, im->w, sizeof(rgba_t))))
{
Bark("CmapToCpack", mfailed);
return -1;
}
pack_cmap(im->cmap);
mp = im->cmap->p_h.rgba;
ras = m[0];
for (cis = ci + im->h * im->w; ci < cis; ci++, ras++)
*ras = *(mp + ((*ci) & mask));
return fill_image_struct(im, m, im->h, im->w,
((im->type == T_GMAP || im->type == T_BW) ? T_GRAY : T_RGBA));
}
/***** convert from RGBA to grayscale ****/
int
rgb2gray(int r, int g, int b)
{
return RGB2GRAY(r, g, b);
}
int
cpack2gray(rgba_t c)
{
return CPACK2GRAY(c);
}
static int
cpack_to_gray(IPTR im)
{
register rgba_t *p = im->raster, *ps;
int total = im->w * im->h;
if (!IS_RGBGRAY(im))
{
for (ps = p + total; p < ps; p++)
{
*p = CPACK2GRAY(*p);
*p = RGB2CPACK(*p, *p, *p);
}
im->type = T_GRAY;
}
return 0;
}
/***** map based grayscale will turn into T_GMAP ****/
static int
cmap_to_mgray(IPTR im)
{
register rgba_t p;
register pc_t *r = im->cmap->ct[0], *rs;
register pc_t *g = im->cmap->ct[1];
register pc_t *b = im->cmap->ct[2];
if (!IS_MGRAY(im))
{
for (rs = r + im->cmap->colors; r < rs; r++, g++, b++)
{
p = RGB2GRAY(*r, *g, *b);
*r = *g = *b = (pc_t) p;
}
im->cmap->packed = 0; /* very important */
im->type = T_GMAP;
}
return 0;
}
static int
cmap_to_gray(IPTR im)
{
return (cmap_to_mgray(im) < 0 || cmap_to_cpack(im) < 0) ? -1 : 0;
}
#if 0 /*** {***/
int
img_to_gray(IPTR im)
{
int status;
if (!im || !im->raster)
return -1;
if (IS_GRAY(im))
return 0;
status = (IS_CPACK(im)) ?
cpack_to_gray(im) : cmap_to_mgray(im);
text_to_gray();
sgf_to_gray();
update_image_info(im);
return status;
}
#endif /****}**/
/* ARGSUSED */
static int
noop(IPTR im)
{
return 0;
}
static int
cpack_to_gmap(IPTR im)
{
return (cpack_to_gray(im) < 0 || rgb_to_cmap(im) < 0) ? -1 : 0;
}
static int
cpack_to_bw(IPTR im)
{
return img_half_toning(im);
}
/********* convert to cpack and then to B&W **************/
static int
cmap_to_bw(IPTR im)
{
return (cmap_to_cpack(im) >= 0) ? cpack_to_bw(im) : -1;
}
/******************************************************
* Global interface to convert image types
*
******************************************************/
/*
* Set up the type conversion matrix, with proper provisions for T_FLEX and
* other types, then everything is reduced to a table lookup
*/
typedef int (*fcnvt) (IPTR);
typedef int (*fprecnvt) (void);
typedef struct
{
IMG_TYPE from, to; /* image types */
fprecnvt precnvt; /* if preparation is needed */
fcnvt cnvt; /* do it */
} convt_s;
static convt_s conv[] =
{
/* start with colormapped images */
{T_CMAP, T_RGBA, 0, cmap_to_cpack},
{T_CMAP, T_GMAP, 0, cmap_to_mgray},
{T_CMAP, T_GRAY, 0, cmap_to_gray},
{T_CMAP, T_BW, pre_bw, cmap_to_bw},
{T_CMAP, T_RGB, 0, cmap_to_rgb},
/* start with graymap images */
{T_GMAP, T_GRAY, 0, cmap_to_cpack},
{T_GMAP, T_RGBA, 0, cmap_to_cpack},
{T_GMAP, T_BW, pre_bw, cmap_to_bw},
{T_GMAP, T_CMAP, 0, noop},
{T_GMAP, T_RGB, 0, cmap_to_rgb},
/* start with cpack */
{T_RGBA, T_GRAY, 0, cpack_to_gray},
{T_RGBA, T_CMAP, pre_quant, rgb_to_cmap},
{T_RGBA, T_GMAP, pre_quant, cpack_to_gmap},
{T_RGBA, T_BW, pre_bw, cpack_to_bw},
{T_RGBA, T_RGB, 0, cpack_to_rgb},
/* start with RGBA gray */
{T_GRAY, T_RGBA, 0, noop},
{T_GRAY, T_GMAP, pre_quant, rgb_to_cmap},
{T_GRAY, T_CMAP, pre_quant, rgb_to_cmap},
{T_GRAY, T_BW, pre_bw, cpack_to_bw},
/* start with B&W */
{T_BW, T_GMAP, 0, noop},
{T_BW, T_CMAP, 0, noop},
{T_BW, T_GRAY, 0, cmap_to_cpack},
{T_BW, T_RGBA, 0, cmap_to_cpack},
/* sentinels */
{T_INVALID, T_INVALID, 0}
};
int
img_convert_type(IPTR im, IMG_TYPE newtype)
{
register int found;
register convt_s *p;
int cancel = 0;
if (!im || !im->raster)
{
Bark("TypeConvert", "Bogus input");
return -1;
}
/* check for special cases: A Flex type means any type is ok */
if (newtype == T_FLEX || im->type == newtype)
return 0;
/* we'll be generating new colormaps */
if (newtype == T_CMAP || newtype == T_GMAP)
im->cmap->packed = 0;
/* get the appropriate function */
for (p = &conv[0], found = 0; !found && p->from != T_INVALID; p++)
found = p->from == im->type && p->to == newtype;
if (!found)
{
to_be_written("ConvertType");
return 1;
}
/* last chance to back off, indicated by cancel */
if ((--p)->precnvt)
cancel = p->precnvt();
return cancel ? -1 : p->cnvt(im);
}
/**************************************************************
* switch black and white: special service to all bw images
**************************************************************/
void
bw_special(IPTR im)
{
register pc_t b;
register CMPTR m = im->cmap;
if (IS_BW(im))
{
b = m->ct[0][0];
m->ct[0][0] = m->ct[1][0] = m->ct[2][0] = m->ct[0][1];
m->ct[0][1] = m->ct[1][1] = m->ct[2][1] = b;
set_cmap(m);
}
}
/*****************************************************************
* normalize a primary color vector of length n to within
* (imin, imax)
*******************************************************************/
void
normal_pc_vec(pc_t *vec, int n, int imin, int imax)
{
register pc_t *v = vec, *vs;
register int vmin, vmax;
register float a, b;
vmin = vmax = *v;
for (vs = v + n; v < vs; v++)
{
if (vmin > *v)
vmin = *v;
if (vmax < *v)
vmax = *v;
}
if (imin == vmin && imax == vmax) /* already normalized */
return;
if (vmax == vmin) /* bad stuff */
return;
a = ((float) imax - imin) / (float) (vmax - vmin);
b = imin - a * vmin;
for (v = vec; v < vs; v++)
*v = ((float) *v * a + b + 0.5);
return;
}
/* interpolate a primary color vector of n to np */
void
interpolate_pc_vec(register pc_t *vin, int n,
register pc_t *vout, int np)
{
register float sx, dx, f;
int kx, kx1;
int i;
sx = (float) (n - 1.0) / (np - 1.0);
for (i = 0; i < np; i++)
{
dx = sx * i;
kx = dx;
dx = dx - kx;
kx1 = kx + (kx < n - 1);
f = vin[kx];
f += dx * (vin[kx1] - f);
vout[i] = (f + 0.5);
}
return;
}
/*
* Default colormaps for bit in displaying edge maps and other
* stuff.
*/
typedef struct
{
int n; /* no. of entries */
pc_t red[PCMAX];
pc_t grn[PCMAX];
pc_t blue[PCMAX];
short pcmin, pcmax; /* channel range */
short rave, gave, bave; /* average in map */
}
DefMap;
#define MAXDEFMAP 18
static DefMap *defmaps[MAXDEFMAP];
static int nmap;
static int
load_defmap_fp(FILE * fp)
{
int done, k, n;
register pc_t *r, *g, *b, *rs;
register long rave, gave, bave;
DefMap *m;
k = nmap;
do
{
done = (n = readpint(fp)) < 0;
if (!done)
{
m = defmaps[k] = malloc(sizeof(*defmaps[0]));
m->n = n;
m->pcmin = readpint(fp);
m->pcmax = readpint(fp);
r = m->red;
g = m->grn;
b = m->blue;
rave = gave = bave = 0;
for (rs = r + n; r < rs; r++, g++, b++)
{
rave += (*r = readpint(fp));
gave += (*g = readpint(fp));
bave += (*b = readpint(fp));
}
m->rave = rave / n;
m->gave = gave / n;
m->bave = bave / n;
++k;
}
}
while (!done);
return k - nmap;
}
static int
load_defmap(void)
{
static const char *mapfile = "defmap.map";
FILE *fp;
int k = 0;
if (nmap > 0)
return nmap;
/* try system directory first */
if ((fp = get_HELPfile_fp(mapfile, "r")))
{
k = nmap = load_defmap_fp(fp);
#ifdef MDEBUG
M_debug("DefMap", "Got %d maps from SysDir", nmap);
#endif
fclose(fp);
}
/* user directory */
if ((fp = get_BITfile_fp(mapfile, "r")))
{
k = k + load_defmap_fp(fp);
#ifdef MDEBUG
M_debug("DefMap", "Got %d maps from UserDir", k - nmap);
#endif
nmap = k;
fclose(fp);
}
M_info("LoadDefMap", "Total %d maps found", nmap);
return nmap;
}
/*
* get default map. a zero indicates grayscale map. Colormap so generated
* will always have the correct range regardless how many entries are
* requested
*/
void
get_bit_defmap(int n, register pc_t *r, register pc_t *g,
register pc_t *b, int req)
{
int i;
register pc_t *rr, *gg, *bb;
DefMap *m;
if (!nmap)
nmap = load_defmap();
#ifdef MDEBUG
M_debug("DefMap", "Getting %d from total of %d", n, nmap);
#endif
if (n < 0 || n > nmap)
n = 0;
if (n == 0)
{ /* default gray */
float a = (float) PCMAXV / req;
for (i = 0; i < req; i++, r++, g++, b++)
*r = *g = *b = (a * i + 0.5);
return;
}
--n;
m = defmaps[n];
rr = m->red;
gg = m->grn;
bb = m->blue;
/* check if map is normalized */
if ((m->pcmax - PCMAXV) || (m->pcmin != 0))
{
normal_pc_vec(rr, m->n, 0, PCMAXV);
normal_pc_vec(gg, m->n, 0, PCMAXV);
normal_pc_vec(bb, m->n, 0, PCMAXV);
m->pcmin = 0;
m->pcmax = PCMAXV;
}
/* check if length match */
if (req != m->n)
{
interpolate_pc_vec(rr, m->n, r, req);
interpolate_pc_vec(gg, m->n, g, req);
interpolate_pc_vec(bb, m->n, b, req);
if (req > m->n && req < PCMAX)
{
memcpy(rr, r, sizeof(pc_t) * req);
memcpy(gg, g, sizeof(pc_t) * req);
memcpy(bb, b, sizeof(pc_t) * req);
m->n = req;
}
}
else
{
memcpy(r, rr, sizeof(pc_t) * req);
memcpy(g, gg, sizeof(pc_t) * req);
memcpy(b, bb, sizeof(pc_t) * req);
}
return;
}
int
get_number_of_defmaps(void)
{
return nmap ? nmap : (nmap = load_defmap());
}
/* set format specific info */
void
set_iformat_info(IPTR im, const char *s)
{
if (s && im)
Strncpy(im->misc, s, MAXINFO - 1);
}
/******************************************************************
* read a piece of framebuffer and save it to the image in core mem.
* Also clip to the edge of the image.
*
* Due to the way geomtric figure is handled in bit, negative w and/or h
* is possible.
******************************************************************/
void
fb_to_ras(IPTR im, const Rect_t * r)
{
char **pp;
const Rect_t *o, *u;
static const char *fn = "FB2RAS";
long odraw = getdrawmode();
Rect_t tmp;
drawmode(NORMALDRAW);
/*
* read it only if overlap. Also we should not allow the src rectangle to
* be outside of the window
*/
copy_rect(&tmp, r);
u = union_rect(canonicalize_rect(&tmp), img_rect(im));
if (u && (o = union_rect(u, make_rect(0, 0, win_w - 1, win_h - 1))))
{
Rect_t p;
/*
* always a good idea to copy the union as other routines might
* overwrite the static buffer
*/
copy_rect(&p, o);
M_info(fn, "x=%d y=%d w=%d h=%d", p.x, p.y, p.w, p.h);
pp = get_mat(p.h, p.w, im->esize);
Rectread(p.x, p.y, p.x + p.w - 1, p.y + p.h - 1, pp[0]);
put_subimage(im, pp, &p, 0);
free_mat(pp);
}
else
{
M_info(fn, "no overlap");
}
drawmode(odraw);
}
/*
* To minimize interference with window manager, we manipulate the
* image so that some of the wm colors are preserved. Different wm
* may use different schemes for lut, we will have to give the color
* in RGB representation and search the default colormap for the index
* For images where index has meaning, e.g., intensity, etc., set
* preserve_wm_colors to zero to supress color swapping.
*/
typedef struct
{
rgba_t rgba; /* colors to preserve */
ci_t ci; /* its index in default sysmap */
}
Wmcolor;
/*
* because collision is not taken care of, the colors that must be
* preserved should be the last one in table
*/
static Wmcolor wmcolor[] =
{
{0x00ffffff, 0}, /* white */
{0, 0} /* black */
};
#define SwapMapEntry(m, i, j) \
do { int r_, g_, b_; \
r_ = m->ct[0][i]; \
g_ = m->ct[1][i]; \
b_ = m->ct[2][i]; \
m->ct[0][i] = m->ct[0][j]; \
m->ct[1][i] = m->ct[1][j]; \
m->ct[2][i] = m->ct[2][j]; \
m->ct[0][j] = r_; \
m->ct[1][j] = g_; \
m->ct[2][j] = b_; \
} while (ZERO)
#define DOGROUP /* if more than 1 reserved color, define this */
void
img_preserve_wm_colors(IPTR im)
{
static int first = 1;
int tran;
int i, pr, pg, pb, k;
int npreserve = sizeof(wmcolor) / sizeof(wmcolor[0]);
CMPTR m = im->cmap;
#ifdef DOGROUP
ci_t allt[MAXCML];
/* initialize the transformation table */
for (i = 0; i < m->colors; i++)
allt[i] = i;
#endif
/* search the default system map for black and white */
if (first)
{
int r, g, b;
unsigned long mdiff, cdiff;
for (k = 0; k < npreserve; k++)
{
Unpack(wmcolor[k].rgba, pr, pg, pb);
mdiff = ~0;
for (i = 0; i < 15; i++)
{
r = (pr - sysmap[0][i]);
g = (pg - sysmap[1][i]);
b = (pb - sysmap[2][i]);
cdiff = 3 * r * r + 4 * g * g + 2 * b * b;
if (cdiff < mdiff)
{
wmcolor[k].ci = i;
mdiff = cdiff;
}
}
}
first = 0;
}
/*
* re-arrange the colormap so that requested colors to be preserved. have
* the same index in current map and system map could be slow if
* npreserve is large
*/
for (k = 0; k < npreserve; k++)
{
/*
* since we only will use m->colors, there is no point to preserve
* colors indeces that are larger than m->colors
*/
if (wmcolor[k].ci >= m->colors)
continue;
Unpack(wmcolor[k].rgba, pr, pg, pb);
tran = cmap_closematch(m, pr, pg, pb);
#ifdef MDEBUG
M_debug("PreserveWMcol", "Need r=%d g=%d b=%d at %d",
pr, pg, pb, wmcolor[k].ci);
M_debug("PreserveWMcol", "Found r=%d g=%d b=%d at %d with total %d",
m->ct[0][tran], m->ct[1][tran], m->ct[2][tran], tran,
m->colors);
#endif
#ifndef DOGROUP
if (tran != wmcolor[k].ci)
{
modify_ci_pixel(im->raster, im->w * im->h,
wmcolor[k].ci, tran, 1);
SwapMapEntry(m, wmcolor[k].ci, tran);
}
#else
if (tran != wmcolor[k].ci)
{
allt[allt[wmcolor[k].ci]] = tran;
allt[tran] = wmcolor[k].ci;
SwapMapEntry(m, wmcolor[k].ci, tran);
}
#endif
}
/* reset pack flag */
im->cmap->packed = 0;
#ifdef DOGROUP
modify_all_ci(im->raster, im->w * im->h, allt);
#endif
}
#if 0 /* UNTESTED {** */
/*
* Instead of preserve a few selected color, we try to preserval
* all colors, upto 256
*/
void
preserve_all_colors(IPTR im)
{
ci_t tran[MAXCML];
int used[MAXCML];
CMPTR m = im->cmap;
int i, j, k;
progress_report("", 100);
for (i = 0; i < m->colors; i++)
{
tran[i] = i;
used[i] = 0;
}
for (i = 0; i < m->colors; i++)
{
j = cmap_closematch(m, sysmap[0][i],
sysmap[1][i], sysmap[2][i]);
if (!used[j] && !used[i] && i != j)
{
tran[tran[i]] = j;
tran[j] = i;
SwapMapEntry(m, i, j);
}
used[j] = 1;
}
m->packed = 0;
modify_all_ci(im->raster, im->w * im->h, tran);
remove_progress_report();
}
#endif /** } **/
/********************************************************************
* simulate double buffering
*
* Normal Frame buffer and over/pup buffer
*
*******************************************************************/
#ifndef NO_NP_DBL
/***********************************************************
* Move an object given by obj having (w,h) as its dimension
* from (ox, oy) to (x,y).
***********************************************************/
void
sim_dbl_swap(IPTR im, int w, int h, void *obj,
int x, int y, int ox, int oy, int mop)
{
int x1, x2, y1, y2;
int sw, sh;
static void **scrn;
set_mat_op(mop);
/* if almost disjoint, not point simulating double buffering */
if (Abs(ox - x) > (0.8 * w) && Abs(oy - y) > (0.8 * h))
{
void **pp;
/* erase the old one */
img_rect_redraw(im, ox, oy, w, h);
/* draw the new one */
pp = no_fail_get_subimage(im, x, y, w, h);
put_submat(pp, h, w, obj, 0, 0, h, w, im->esize);
PI_rectwrite(x, y, x + w - 1, y + h - 1, pp[0]);
free_mat(pp);
}
else
{
x1 = Min(ox, x) - 1;
x2 = Max(ox, x) + w + 1;
y1 = Min(oy, y) - 1;
y2 = Max(oy, y) + h + 1;
sw = x2 - x1 + 1;
sh = y2 - y1 + 1;
free_mat(scrn);
scrn = no_fail_get_subimage(im, x1, y1, sw, sh);
put_submat(scrn, sh, sw, obj, y - y1, x - x1, h, w, im->esize);
PI_rectwrite(x1, y1, x1 + sw - 1, y1 + sh - 1, scrn[0]);
}
set_mat_op(O_NONE);
}
void
mv_ras_obj(void **obj, int w, int h, int ox, int oy, int x, int y)
{
/* sanity checks */
if (!obj || !obj[0] || w <= 0 || h <= 0)
{
Bark("MovRasObj", "Bad input");
return;
}
if (double_buf)
{
PI_rectwrite(x, y, x + w - 1, y + h - 1, obj[0]);
swapbuffers();
img_rect_redraw(imgptr, ox, oy, w, h);
}
else
{
sim_dbl_swap(imgptr, w, h, obj, x, y, ox, oy, O_NONE);
}
}
#endif /* !NO_NP_DBL */
/********************************************************************
* Get same color but with different intensity.
* Also subject (cmin, cmax) check
********************************************************************/
void
scale_color(int col[], float factor, int scaled[], int cmin, int cmax)
{
if (IS_CPACK(imgptr))
{
scaled[0] = col[0] * factor;
scaled[1] = col[1] * factor;
scaled[2] = col[2] * factor;
}
else if (IS_CI(imgptr))
{
CMPTR m = imgptr->cmap;
int ci = col[4];
Range(ci, 0, m->colors - 1);
scaled[0] = factor * m->ct[0][ci];
scaled[1] = factor * m->ct[1][ci];
scaled[2] = factor * m->ct[2][ci];
}
Range(scaled[0], cmin, cmax);
Range(scaled[1], cmin, cmax);
Range(scaled[2], cmin, cmax);
/* make sure we return valid PC */
Range(scaled[0], 0, PCMAXV);
Range(scaled[1], 0, PCMAXV);
Range(scaled[2], 0, PCMAXV);
if (IS_CI(imgptr))
{
scaled[3] = cmap_closematch(imgptr->cmap,
scaled[0],
scaled[1],
scaled[2]);
}
}
/*******************************************************************
* Return a one-liner info about image im
******************************************************************/
const char *
image_vital_info(IPTR im)
{
static char buf[1024];
sprintf(buf, "(%dX%d %s)", im->w, im->h, im->key);
return buf;
}
#if 0 /** } **/
/*********************************************************************
* Pack a char stream (with padding) to pixel in cpack format
*********************************************************************/
void
char_stream4_cpack(register rgba_t *rgba,
register unsigned char *pc, long total, int rgb)
{
register rgba_t *rs = rgba + total;
if (rgb)
{
for (; rgba < rs;)
{
pc++; /* skip alpha */
*rgba++ = Pack(*pc, *(pc + 1), *(pc + 2));
pc += 3;
}
}
else
{
/* in RGB order */
for (; rgba < rs;)
{
pc++; /* skip alpha */
*rgba++ = Pack(*(pc + 2), *(pc + 1), *pc);
pc += 3;
}
}
}
/********************************************************************
* Pack a char stream (no padding) bytes into a pixel
********************************************************************/
void
char_stream3_cpack(rgba_t *rgba, unsigned char *pc, long total, int rgb)
{
register rgba_t *rs = rgba + total;
if (rgb)
{
for (; rgba < rs;)
{
*rgba++ = Pack(*pc, *(pc + 1), *(pc + 2));
pc += 3;
}
}
else
{
/* in GBR order */
for (; rgba < rs;)
{
*rgba++ = Pack(*(pc + 2), *(pc + 1), *pc);
pc += 3;
}
}
}
#endif /** } **/
/**********************************************************************
* Anti-aliasing
* It doesn't look right if linewidth != 1 or the linestyle is
* not solid
*********************************************************************/
void
smooth_line_on(IPTR im, int *col, int lw)
{
Color4(col);
if (smooth_lines && getlstyle() == 0 &&
sml_capable() && lw == 1 && IS_CPACK(im))
{
pntsmooth(SMP_ON | SMP_SMOOTHER);
linesmooth(SML_ON | SML_SMOOTHER | SML_END_CORRECT);
blendfunction(BF_SA, BF_MSA);
subpixel(1);
cpack(Pack(col[0], col[1], col[2]) | 0xff000000);
}
}
/*********** Turn anti-aliasing off ******************/
void
smooth_line_off(IPTR im, int *col)
{
pntsmooth(SMP_OFF);
linesmooth(SML_OFF);
blendfunction(BF_ONE, BF_ZERO);
subpixel(0);
if (IS_CPACK(im))
cpack(Pack(col[0], col[1], col[2]));
}
/************************************************************
* Convert X11 bitmap to GL 32x32 patterns
************************************************************/
#define getbits(a, k, i) ((a[(k)] >> (i))&1)
#define GL_pat unsigned short
GL_pat *
XBM_to_GL_pat(int w, int h, char *bits)
{
GL_pat *pat, *cpat;
int i, j, k, on, total;
if (w != 32 || h != 32 || !bits)
{
M_err("XBM2GLPAT", "Bad input w=%d h=%d", w, h);
return 0;
}
total = (w / 16) * h;
/* do the conversion */
pat = calloc((total + 1), sizeof(*pat));
cpat = pat + total - 1;
for (k = j = 0; j < h; j++)
{
for (i = 0; i < w; i++)
{
on = (bits[k + i / 8] & (1 << (i % 8))) != 0;
*(cpat - (i / 16)) |= on << (15 - (i % 16));
}
k += (w + 7) / 8;
cpat -= (w + 15) / 16;
}
return pat;
}
const Rect_t *
img_rect(IPTR im)
{
static Rect_t dummy[5];
static int np;
Rect_t *r;
r = dummy + np++;
r->x = im->xi;
r->y = im->yi;
r->w = im->w;
r->h = im->h;
np %= 5;
return r;
}